<?php

/*
|--------------------------------------------------------------------------
| Application Routes
|--------------------------------------------------------------------------
|
| Here is where you can register all of the routes for an application.
| It's a breeze. Simply tell Laravel the URIs it should respond to
| and give it the controller to call when that URI is requested.
|
*/
//创建一个项目名为 blog 的项目
//composer create-project laravel/laravel blog "5.1.*"

Route::get('/', function () {
    return view('welcome');
});

/*
Route::get('/hello',function(){
	return 'hello laravel[GET]!';
});
*/
Route::get('/testPost',function(){
    $csrf_token = csrf_token();
    $form = <<<FORM
        <form action="hello" method="POST">
            <input type="hidden" name="_token" value="{$csrf_token}">
            <input type="submit" value="Test"/>
        </form>
FORM;
    return $form;
});

Route::post('hello',function(){
	$a = $_POST['_token'];
    return "Hello Laravel[POST]!{$a}";
});

/*

//还可以使用Route门面上的match方法匹配多种请求方式：

Route::match(['get','post'],'/hello',function(){
   return "Hello Laravel!";
});

//当然还使用更方便的any方法匹配所有请求方式：

Route::any('/hello',function(){
    return "Hello Laravel!";
});

//效果都一样。
*/

//必选参数   定义了参数必须传参
//Route::get('/hello/{name}/{age}',function($name,$age){
//	return "我的名字是{$name},年龄{$age}";
//});
//可选参数
Route::get('/hello/{name?}',function($name="张三"){
	return "我是{$name}";
});

//正则约束

//有时候我们希望对路由有更加灵活的条件约束，可以通过正则表达式来实现：

Route::get('/hello/{name?}',function($name="Laravel"){
    return "Hello {$name}!";
})->where('name','[A-Za-z]+');

//该条件约束意味着$name参数只能包含大小写字母，如果包含数字或中文就会抛出NotFoundHttpException异常。

//如果我们想要在全局范围内对参数进行条件约束，可以在WWW\blog\app\Providers\RouteServiceProvider的boot方法中做如下定义：
/*
public function boot(Router $router)
{
    $router->pattern('name','[A-Za-z]+');
    parent::boot($router);
}
*/
//我们访问http://laravel.app:8000/hello/Laravel123/by/Laravel//学院时一样会抛出NotFoundHttpException异常。这意味着boot方法定义的参数条件约束将会应用到所有包含该参数的路//由中。

//此外，服务提供者的boot方法在所有服务提供者的register方法执行完毕后开始执行，也就是说，我们可以在boot方法对任意服务容器中的对象进行依赖注入。




//路由命名---给路由起个名字  -----也可以带参数
Route::get("/flg/getname/{name}",['as'=>'gn',function($name){
	return "O(∩_∩)O哈哈~{$name}";
}]);

//Route::get("/goto",function(){
//	return route('gn');     //route()生成指向该路由的URL或者生成跳转到该路由的重定向链接
//});
									//传参
Route::get("/goto",function(){
	return redirect()->route('gn',['name'=>'zhangsan']);    //跳转到指定URL地址
});


//路由分组时路由命名方式

//再来看一个更复杂的例子，使用路由分组时如何定义路由命名？官网文档提供的例子如下：

Route::group(['as' => 'admin::'], function () {
    Route::get('dashboard', ['as' => 'dashboard', function () {
        //
    }]);
});

//在Route门面的group方法中使用一个as关键字来指定该路由群组中所有路由的公共前缀，然后再在里面每个路由中使用//as关键字为该路由命名。

//这样我们可以通过如下方式来生成该路由URL：

Route::get('/testNamedRoute',function(){
    return route('admin::dashboard');
});



//2、路由分组

//路由分组就是将一组拥有相同属性（中间件、命名空间、子域名、路由前缀等）的路由使用Route门面的group方法聚合起来。

//中间件

//首先我们在(项目名下)应用根目录下运行如下Artisan命令生成一个测试用的中间件TestMiddleware：
//php artisan make:middleware TestMiddleware

//这样会在/app/Http/Middleware目录下生成一个TestMiddleware.php文件，打开该文件编辑TestMiddleware类的handle方法如下：
/*public function handle($request, Closure $next)
{
    if($request->input('age')<18)
        return redirect()->route('refuse');
    return $next($request);
}*/

//然后我们打开/app/Http/Kernal.php文件，新增TestMiddleware到Kernel的$routeMiddleware属性：
//'test' => \App\Http\Middleware\TestMiddleware::class,

//接下来我们在routes.php中定义路由如下：
Route::group(['middleware'=>'test'],function(){
	Route::get('/write/laravel',function(){
		//使用中间件
	});
	Route::get('/update/laravel',function(){
		//使用中间件
	});
});
Route::get('/age/refuse',['as'=>'refuse',function(){
	return "未成年人禁止入内！";
}]);
//这样当我们在浏览器中访问http://localhost/blog/public/index.php/write/laravel?age=15或者http://localhost/blog/public/index.php/update/laravel?age=15时就会跳转到http://localhost/blog/public/index.php/age/refuse，并显示：

//未成年人禁止入内！


//命名空间
//默认情况下，routes.php中的定义的控制器位于App\Http\Controllers命名空间下，所以如果要指定命名空间，只需指定App\Http\Controllers之后的部分即可：

Route::group(['namespace'=>'Laraveldemo'],function(){
	//控制器在 App\Http\Controllers\Laraveldemo  命名空间下
	Route::group(['namespace'=>'DDOS'],function(){
		//控制器在App\Http\Controllers\Laraveldemo\DDOS  命名空间下
	});
});


//子域名 ---么看到效果

Route::group(['domain'=>'{$service}.laravel.app'],function(){
	Route::get('/writes/laravel',function($service){
		return "write from {$service}.laravel.app";
	});
	Route::get('/updates/laravel',function($service){
		return "updates feom {$service}.laravel.app";
	});
});
///这样我们在浏览器中访问http://write.laravel.app:8000/write/laravelacademy，则输出

//Write FROM write.laravel.app

//访问http://update.laravel.app:8000/write/laravelacademy时，则输出：

//Write FROM update.laravel.app

//注意：要想让子域名解析生效，需要在hosts中绑定IP地址




//2.4 路由前缀

//如果路由群组中的所有路由包含统一前缀，则我们可以通过在group方法中设置prefix属性来指定该前缀：

Route::group(['prefix'=>'laravelacademy'],function(){
    Route::get('write',function(){
        return "Write LaravelAcademy";
    });
    Route::get('update',function(){
        return "Update LaravelAcademy";
    });
});

//这样我们就可以通过http://laravel.app:8000/laravelacademy/write或者http://laravel.app:8000/laravelacademy/update来访问对应的操作。

//我们甚至还可以在路由前缀中指定参数：

Route::group(['prefix'=>'laravelacademy/{version}'],function(){
    Route::get('write',function($version){
        return "Write LaravelAcademy {$version}";
    });
    Route::get('update',function($version){
        return "Update LaravelAcademy {$version}";
    });
});

//这样我们在浏览器中访问http://laravel.app:8000/laravelacademy/5.1/write，则对应会输出：

//Write LaravelAcademy 5.1


//CSRF_token 的使用   防止跨域请求攻击

//csrf_token() //生成一个token值
//csrf_field() //生成一个input隐藏域：<input type="hidden" name="_token" value="edYhzn2ZOpcs7LmEatDM8dZPmXQyO4HBo8CCPd4y">

Route::get('/test',function(){
	$csrf_field = csrf_field();
	$test = <<<GET
	<form action="testOne" method="post">
		{$csrf_field}
		<input type="submit" value="Test">
	</form>
GET;
	return $test;
});

Route::post('testOne',function(){
	return "Success!";
});

//如果没有csrf_token 则点击“Test”按钮，则抛出TokenMismatchException异常。

//3、从CSRF验证中排除指定URL

//并不是所有请求都需要避免CSRF攻击，比如去第三方API获取数据的请求。

//通过在VerifyCsrfToken（app/Http/Middleware/VerifyCsrfToken.php）中间件中将要排除的请求URL添加到$except属性数组中：

        /**
         * 指定从 CSRF 验证中排除的URL
         *
         * @var array
         */
//        protected $except = [
//            'testOne'
//        ];

//这样我们刷新页面，再次在http://localhost/blog/public/index.php/test页面中点击“Test”按钮，则页面不会报错，正常输出如下内容：Success!


//4、X-CSRF-Token及其使用

//如果使用Ajax提交POST表单，又该如何处理呢？我们可以将Token设置在meta中：

//<meta name="csrf-token" content="{{ csrf_token() }}">

//然后在全局Ajax中使用这种方式设置X-CSRF-Token请求头并提交：
/*
$.ajaxSetup({
    headers: {
        'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
    }
});
*/
//Laravel的VerifyCsrfToken中间件会检查X-CSRF-TOKEN请求头，如果该值和Session中CSRF值相等则验证通过，否则不通过。

//5、X-XSRF-Token及其使用

//除此之外，Laravel还会将CSRF的值保存到名为XSRF-TOKEN的Cookie中，然后在VerifyCsrfToken中间件验证该值，当然，我们不需要手动做任何操作，一些JavaScript框架如Angular会自动帮我们实现。



//6、Laravel中CSRF验证原理分析

//分析下源码，看看Laravel底层到底是如何避免CSRF攻击的：

//1）首先Laravel开启Session时会生成一个token值并存放在Session中（Illuminate\Session\Store.php第90行start方法）
//2）然后重点分析VerifyToken中间件的handle方法，该方法中先通过isReading方法判断请求方式，如果请求方法是HEAD、GET、OPTIONS其中一种，则不做CSRF验证；

//3）再通过shouldPassThrough方法判断请求路由是否在$excpet属性数组中进行了排除，如果做了排除也不做验证；

//4）最后通过tokensMatch方法判断请求参数中的CSRF TOKEN值和Session中的Token值是否相等，如果相等则通过验证，否则抛出TokenMismatchException异常。

//注：tokensMatch方法首先从Request中获取_token参数值，如果请求中不包含该参数则获取X-CSRF-TOKEN请求头的值，如果该请求头也不存在则获取X-XSRF-TOKEN请求头的值，需要注意的是X-XSRF-TOKEN请求头的值需要调用Encrypter的decrypt方法进行解密。

//-=-=====================================

//1、中间件简介

//Laravel中可以把HTTP中间件看做“装饰器”，在请求到达最终动作之前对请求进行过滤和处理。

//中间件在Laravel中有着广泛的应用，比如用户认证、日志、维护模式、开启Session、从Session中获取错误信息，以及上一篇教程中提到的CSRF验证，等等。

//中间件类默认存放在app/Http/Middleware目录下。
//
//2、中间件创建及其使用

//我们在《HTTP路由实例教程（二）—— 路由命名和路由分组》一文已经演示了如何创建中间件以及中间件的基本使用。

//自定义中间件类只需要定义一个handle方法即可，然后我们将主要业务逻辑定义在该方法中，如果我们想在请求处理前执行业务逻辑，则在$next闭包执行前执行业务逻辑操作：
//public function handle($request, Closure $next)
//{
//    // 执行业务逻辑操作

//    return $next($request);
//}

//如果想要在请求处理后执行中间件业务逻辑，则在$next闭包执行后执行操作：
/*
public function handle($request, Closure $next)
{
    $response = $next($request);

    // 执行动作

    return $response;
}
*/


/**  这个表示没看懂  2
3、中间件参数

除了请求实例$request和闭包$next之外，中间件还可以接收额外参数，我们还是以TestMiddleware为例，现在要求年龄在18岁以上的男性才能访问指定页面，handle方法定义如下：

public function handle($request, Closure $next, $gender)
{
    if($request->input('age')>=18 && $gender==$request->input('gender')){
        return $next($request);
    }else{
        return redirect()->route('refuse');
    }
}

对应的路由配置如下：

Route::group(['middleware'=>'test:male'],function(){
    Route::get('/write/laravelacademy',function(){
        //使用Test中间件
    });
    Route::get('/update/laravelacademy',function(){
        //使用Test中间件
    });
});

Route::get('/age/refuse',['as'=>'refuse',function(){
    return "18岁以上男子才能访问！";
}]);


4、定义可终止的中间件

可终止的中间件是指定义了terminate方法的中间件，terminate方法会在一次请求生命周期的末尾执行一些操作。比如StartSession中间件定义了该方法，在响应数据发送到浏览器之后将session数据保存起来。

可终止的中间件需要追加到app/Http/Kernel.php类的全局中间件列表即$middleware属性数组中。

调用中间件的terminate方法时，Laravel会从服务容器中取出新的中间件实例，所以如果想要调用handle方法和terminate方法时使用的是同一个中间件实例，需要使用singleton方法将该中间件注册到服务容器。


**/



//HTTP控制器实例教程 —— 创建RESTFul风格控制器实现文章增删改查

//1、创建RESTFul风格控制器

//创建一个RESTFul风格的控制器用于对博客文章进行增删改查，创建这样的控制器很简单，在应用根目录运行如下Artisan命令即可：

//php artisan make:controller PostController

//该命令会在app/Http/Controllers目录下生成一个PostController.php文件，该控制器内容如下：

//2、为RESTFul风格控制器注册路由

//接下来我们在routes.php文件中为该控制器注册路由：

Route::resource('post','PostController');




//获取Request请求实例
Route::controller("request","RequestController");


//视图响应
//需要在resources/views下新建一个视图文件hello.blade.php，其内容如下：
//{{$message}}

//使用全局帮助函数view：
Route::get('testResponseView',function(){
    $value = 'text/html;charset=utf-8';
    return view('hello',['message'=>'Hello LaravelAcademy']);
});

//2.2 返回JSON/JSONP
//还可以使用json方法返回json格式数据：
Route::get('testResponseJson',function(){
    return response()->json(['name'=>'LaravelAcademy','passwd'=>'LaravelAcademy.org']);
});



//如果返回的是JSONP响应，也很简单，只需要在json后面再调用setCallback即可：

Route::get('testResponseJson',function(){
    return response()->json(['name'=>'LaravelAcademy','passwd'=>'LaravelAcademy.org'])
        ->setCallback(request()->input('callback'));
});



//2.3 文件下载
//使用download方法可生成用于下载给定路径文件的响应，这里我们下载上一节上传的文件：
Route::get('testResponseDownload',function(){
    return response()->download(
        realpath(base_path('public/images')).'/laravel-5-1.jpg',
        'Laravel学院.jpg'
    );
});


//3、 RedirectResponse篇

//3.1 基本重定向
Route::get('dashboard', function () {
    return redirect('home/dashboard');
});

//如果要重定向到上一个位置，则使用back方法：
Route::post('user/profile', function () {
    // 验证请求...
    return back()->withInput();
});


//3.2 重定向到命名路由
//使用route方法重定向到命名路由：
Route::get('/hello/laravelacademy',['as'=>'academy',function(){
    return 'Hello LaravelAcademy';
}]);

Route::get('testResponseRedirect',function(){
    return redirect()->route('academy');
});

//如果命名路由中有参数，那么我们可以在route中传入参数：

Route::get('/hello/laravelacademy/{id}',['as'=>'academy',function($id){
    return 'Hello LaravelAcademy '.$id;
}]);

Route::get('testResponseRedirect',function(){
    return redirect()->route('academy',100);
});



//3.3 重定向到控制器动作

//使用action方法重定向到控制器动作：

//Route::resource('post','PostController');

Route::get('testResponseRedirect',function(){
    return redirect()->action('PostController@index');
});

//在浏览器中访问http://laravel.app:8000/testResponseRedirect，则页面会跳转到http://laravel.app:8000/post并输出对应内容。

//当然也可以传递参数到action方法：

Route::get('testResponseRedirect',function(){
    return redirect()->action('PostController@show',[1]);
});


//3.4 带一次性Session数据的重定向
//使用with方法可以携带一次性session数据到重定向请求页面(一次性session数据即使用后立即销毁的session数据项)：
Route::post('user/profile', function () {
    // 更新用户属性...
    return redirect('dashboard')->with('status', 'Profile updated!');
});
//这种特性通常在提交表单验证失败返回错误信息时很有用。





//1、在视图间共享数据

//除了在单个视图中传递指定数据之外，有时候需要在所有视图中传入同一数据，即我们需要在不同视图中共享数据。要实现这一目的，需要使用视图工厂的share方法。

//全局帮助函数view和前面一节提到的response类似，如果传入参数，则返回Illuminate\View\View实例，不传入参数则返回Illuminate\View\Factory实例。所以我们可以通过在服务提供者的app\Providers\AppServiceProvider\boot方法中使用如下方式实现视图间共享数据：

    /**
     * Bootstrap any application services.
     *
     * @return void
     
    public function boot()
    {
        //视图间共享数据
        view()->share('sitename','Laravel学院');
    }
*/

/*
我们在routes.php中定义两个路由：
Route::get('testViewHello',function(){
    return view('hello');
});

Route::get('testViewHome',function(){
    return view('home');
});

然后在resources/views目录下创建一个home.blade.php视图文件，内容如下：
{{$sitename}}首页

再创建一个hello.blade.php视图文件：
欢迎来到{{$sitename}}！

在浏览器中分别访问http://laravel.app:8000/testViewHello和http://laravel.app:8000/testViewHome，则都能解析出$sitename的值。
*/




//2、视图Composer

//有时候我们想要在每次视图渲染时绑定一些特定数据到视图中，比如登录用户信息，这时候我们就要用到视图Composer，视图Composer通过视图工厂的composer方法实现。该方法的第二个回调参数支持基于控制器动作和闭包函数两种方式。

//简单起见，我们还是基于AppServiceProvider，不去单独创建服务提供者，这里我们传递闭包参数（控制器动作参考视图文档）：
/*
	public function boot()
    {
        //视图间共享数据
        view()->share('sitename','Laravel学院');

        //视图Composer
        view()->composer('hello',function($view){
            $view->with('user',array('name'=>'test','avatar'=>'/path/to/test.jpg'));
        });
    }
*/



//修改hello.blade.php视图文件：

//欢迎来到{{$sitename}}！

//<h3>用户信息</h3>
//用户名:{{$user['name']}}<br>
//用户头像:{{$user['avatar']}}

//在浏览器中访问http://laravel.app:8000/testViewHello，输出内容如下：

//欢迎来到Laravel学院！

//用户信息
//用户名:test
//用户头像:/path/to/test.jpg

/*你也可以传递数据到多个视图：

view()->composer(['hello','home'],function($view){
    $view->with('user',array('name'=>'test','avatar'=>'/path/to/test.jpg'));
});*/

/*甚至所有视图（使用通配符*）：

view()->composer('*',function($view){
    $view->with('user',array('name'=>'test','avatar'=>'/path/to/test.jpg'));
});*/




//使用DB门面操作数据库

/*配置数据库在config/database.php

修改.env文件
DB_CONNECTION=mysql
DB_HOST=[你的数据库地址]
DB_PORT=[端口（3306）]
DB_DATABASE=[数据库]
DB_USERNAME=[用户名]
DB_PASSWORD=[密码]
*/

//use DB;
//执行原生sql     查询返回数组  其他的返回影响的条数
//DB::insert('insert into users (id, name, email, password) values (?, ?, ? , ? )',[1, 'Laravel','laravel@test.com','123']);

//$user = DB::select('select * from users where id = ?', [1]);
//dd($user);
//可以看到在执行查询的时候使用了参数绑定，以避免SQL注入。除此之外还可以使用命名绑定：

//$user = DB::select('select * from users where id = :id', [':id'=>1]);

//$affected = DB::update('update users set name="LaravelAcademy" where name = ?', ['Academy']);
//echo $affected;

//$deleted = DB::delete('delete from users');
//echo $deleted;


//通用语句
//除了上述这些DML（insert/update/delete）和DQL（select）语句，SQL语句还包括DCL（create/drop等）和DDL（grant等），要运行后者，我们可以调用DB门面的statement方法：

//DB::statement('drop table users');



//、监听查询事件
//除此之外，我们还可以通过DB门面的listen方法监听查询事件，比如我们在记录日志和调试的时候这会给我们确定问题提供便利，可以在服务提供者的boot方法中注册该监听器，例如我们在AppServiceProvider的boot方法中定义监听器如下：

/**
* 启动所有应用服务
*
* @return void

public function boot()
{
    DB::listen(function($sql, $bindings, $time) {
        echo 'SQL语句执行：'.$sql.'，参数：'.json_encode($bindings).',耗时：'.$time.'ms';
    });
}

这样我们运行如下SQL语句：

DB::insert('insert into users (id, name, email, password) values (?, ?, ? , ? )',
    [3, 'LaravelAcademy','laravel-academy@test.com','123']);

则浏览器会输出如下内容：

SQL语句执行：insert into users (id, name, email, password) values (?, ?, ? , ? )，参数：[3,"LaravelAcademy","laravel-academy@test.com","123"],耗时：1.26ms
*/


/*
5、数据库事务

很多时候，我们需要执行一连串操作，而其中任何一个操作出错则整个流程失败，需要回退重来，这个时候我们就要用到数据库事务。

DB门面提供两种方式支持数据库事务，一种是调用transaction方法然后传入闭包作为参数，我们将需要进行事务操作的逻辑放到闭包函数内：

DB::transaction(function () {
    DB::table('users')->update(['id' => 1]);
    DB::table('posts')->delete();
});

另一种是 beginTransaction、 rollBack和 commit三个方法一起使用从而构建一个完整的事务操作：

DB::beginTransaction();
if($somethingIsFailed){
    DB::rollback();
    return false;
}
DB::commit();
*/


//查询构建器
//$users = DB::table('users');
/**
DB::table('users')->insert([
    ['id'=>1,'name'=>'Laravel','email'=>'laravel@test.com','password'=>'123'],
    ['id'=>2,'name'=>'Academy','email'=>'academy@test.com','password'=>'123'],
    ['id'=>3,'name'=>'LaravelAcademy','email'=>'laravel-academy@test.com','password'=>'123']
]);


执行成功后即可在数据表$users中插入三条记录。有时候，我们需要插入记录后获取自增ID，可以使用insertGetId方法：

$insertId = DB::table('users')->insertGetId(
    ['name'=>'Laravel-Academy','email'=>'laravelacademy@test.com','password'=>'456']
);


$affected = DB::table('users')->where('name','Laravel-Academy')->update(['password'=>'123']);
该方法返回受影响的函数。

使用delete方法删除表记录，删除方法和更新方法类似，返回被删除的行数：
$deleted = DB::table('users')->where('id', '>', 3)->delete();

如果我们是要删除整个数据表数据，则略去where条件，如果是要清空数据表还要将自增ID置为0，可以使用truncate方法：
DB::table('users')->truncate();


使用get方法即可获取一张表的所有记录：

$users = DB::table('users')->get();
dd($users);


如果是获取指定列的数据，则需要加上select条件：

$users = DB::table('users')->select('name','email')->get();
dd($users);

获取单条记录需要在查询基础上加上first方法：

$user = DB::table('users')->where('name','Laravel')->first();
dd($user);


4.4 获取单列的值

上述方法获取的查询结果都是对象实例/对象实例数组，有时候，我们只是想简单获取单列的值，遍历数组获取指定列的值太麻烦，可以使用lists方法获取列值数组：

$users = DB::table('users')->lists('name');
dd($users);


4.5 原生表达式

此外，查询构建器还支持原生表达式，我们需要调用DB门面的raw方法来实现：

$users = DB::table('users')->select(DB::raw('name,email'))->where('id','<',3)->get();
dd($users);

**/


/**

内连接

内连接用于获取两张表结果集的交集部分，我们可以使用查询构建器的join方法进行内连接查询：

$users = DB::table('users')->join('posts','users.id','=','posts.user_id')->get();
dd($users);


左连接

左连接的结果集指定的左表的所有行，如果左表的某行在右表中没有匹配行，则在相关联的结果集行中右表的所有选择列表列均为空值(null)。我们使用查询构建器的leftJoin方法进行左连接查询：

$users = DB::table('users')->leftJoin('posts','users.id','=','posts.user_id')->get();
dd($users);



3、where子句

使用查询构建器上的where方法可以添加自定义查询条件，调用该方法需要传入三个参数：第一个列名，第二个是操作符，第三个是比较值：

$user = DB::table('users')->where('name','=','Laravel')->get();
dd($user);

如果操作符为“=”，该语句也可简化为：

$user = DB::table('users')->where('name','Laravel')->get();


需要注意的是查询构建器支持方法链，这意味着如果有多个查询条件且这个多个条件是AND连接，可以在get之前使用多个where方法。如果多个条件使用OR连接，可以使用orWhere方法：

$user = DB::table('users')->where('name','Laravel')->orWhere('name','Academy')->get();



4、排序

查询构建器使用orderBy方法对查询结果进行排序：

$users = DB::table('users')->orderBy('id','desc')->get();
dd($users);


分组一般用于聚合查询，接下来我们使用groupBy方法对查询结果进行分组，比如我们可以统计每个分类下有几篇文章：

$posts = DB::table('posts')->select('cat_id',DB::raw('COUNT(id) as num'))->groupBy('cat_id')->get();
dd($posts);



我们还可以使用having方法为分组加上条件，比如我们可以统计总浏览数大于500的分类：

$posts = DB::table('posts')->select('cat_id',DB::raw('SUM(views) as views'))->groupBy('cat_id')->having('views','>',500)->get();
dd($posts);
注意：having中的条件字段必须出现在select查询字段中，否则会报错。


6、分页

查询构建器中使用skip和take对查询结果进行分页，相当于SQL语句中的limit语句：

$posts = DB::table('posts')->skip(0)->take(2)->get();   //从第0条开始查俩条
dd($posts);
**/



//2、定义模型
//2.1 创建模型    一个模型对应一张表   默认Post对应的数据表为posts

//我们使用Artisan命令make:model生成模型类，模型类默认位于app目录下，我们也可以在创建时指定生成目录：

//php artisan make:model Models/Post

//这样就会在app目录下生成一个Models目录，并且在Models目录下生成一个Post模型类。Laravel 中所有模型类继承自Illuminate\Database\Eloquent\Model类。

/*
2.2 指定表名

如果不手动指定，默认Post对应的数据表为posts，以此类推。也可以通过设置$table属性自定义表名：

public $table = 'posts';

2.3 指定主键

Eloquent默认数据表主键为id，当然也可以通过设置$primaryKey属性来自定义主键：

public $primaryKey = 'id';

2.4 时间戳设置

默认情况下，Eloquent模型类会自动管理时间戳列create_at和update_at（如果定义迁移时设置了这两列的话），如果要取消自动管理，可以设置$timestamps属性为false：

public $timestamps = false;

还有，如果你想要设置时间戳的格式，可以使用$dateFormat属性，该属性决定了日期时间以何种格式存入数据库，以及以何种格式显示：

//设置日期时间格式为Unix时间戳     更多关于日期时间格式设置，请参考php官方函数date中format部分。
protected $dateFormat = 'U';
*/

//为控制器注册路由
Route::resource('test','TestController');

//控制器中引入模型类
//use App\Models\Post;
//获取多个Eloquent模型
        //$posts = Post::all();
        //dd($posts);

//每一个Eloquent模型本身都是一个查询构建器，所有我们可以调用所有查询构建器上的方法，只不过第一个方法调用都要使用静态方法调用：

//$posts = Post::where('c_id','<',3)->orderBy('c_id','desc')->take(1)->get();
//dd($posts);

/**
3.2 获取单个模型

可以使用查询构建器方法获取单个模型实例：

$post = Post::where('id',1)->first();
dd($post);

当然也可以通过Eloquent模型类提供的快捷方法find：

$post = Post::find(1);
**/

//如果没有找到对应的表记录，会输出null

/*
3.3 聚合函数查询

如果要对查询结果进行计数、统计、最大值/最小值、平均数等聚合运算，可以使用查询构建器上的对应方法，我们我们查询文章总数：

$count = Post::where('id','>',0)->count();
echo $count;

我们想要获取文章最大阅读数：

$views = Post::where('id','>',0)->max('views');
echo $views;

*/

/*
1、创建模型
1.1 使用save方法创建模型

调用Eloquent模型类的save方法即可创建模型并插入数据到数据库：

$post = new Post;
$post->title = 'test 4';
$post->content = 'test content';
$post->user_id = 1;
$post->cat_id = 1;
if($post->save()){
    echo '添加文章成功！';
}else{
    echo '添加文章失败！';
}
*/

/**
1.2 使用create方法插入数据

除此之外还可以使用create方法插入数据，由于该方法中用到了批量赋值（Mass Assignment），所以我们需要在模型类中设置$fillable属性或者$guarded属性，以表明哪些属性可以通过该方法设置，哪些不可以。

开始之前，我们先解释下什么是批量赋值，以及为什么要使用批量赋值。

批量赋值的英文名称是Mass Assignment，所谓的批量赋值是指当我们将一个数组发送到模型类用于创建新的模型实例的时候（通常是表单请求数据），我们可以简单通过如下方式实现：

$post = Post::create(Input::all());

而不是像使用save方法那样一个一个的设置属性值，


但事物总是相对的，使用批量赋值是很方便，但同时也带来了安全隐患，很多时候模型类的某些属性值不是我们所期望通过批量赋值修改的，比如用户模型有个user_type属性，如果用户通过请求数据将其类型修改为管理员类型，这显然是不允许的，正是基于这一考虑，Eloquent模型类为我们提供了$fillable属性和$guarded属性，我们可以将其分别看作“白名单”和“黑名单”，定义在$fillable中的属性可以通过批量赋值进行赋值，而定义在$guarded中的属性在批量赋值时会被过滤掉。

那么如果我们确实想要修改定义在$guarded中的属性怎么办？答案是使用save方法。

此外需要注意的是$fillable和$guarded方法同时只能定义一个，原因嘛很简单，非黑即白，定义了一个另外一个也就确定了。

可见批量赋值不仅为我们创建模型提供了便利，还避免了安全隐患，提高了系统的安全性。

下面我们来演示一下批量赋值的使用。首先在Post模型中定义$guarded属性如下：

protected $guarded = ['views','user_id','updated_at','created_at'];

然后在控制器中实现创建模型实例的逻辑：

$input = [
    'title'=>'test 5',
    'content'=>'test content',
    'cat_id'=>1,
    'views'=>100,
    'user_id'=>2
];
$post = Post::create($input);
dd($post);


可见user_id和views字段都没有插入进去，这正是$guarded发挥了作用，如果要设置这两个值也很简单：

$input = [
    'title'=>'test 5',
    'content'=>'test content',
    'cat_id'=>1,
    'views'=>100,
    'user_id'=>2
];
$post = Post::create($input);
$post->user_id = 2;
$post->views = 100;
$post->save();
dd($post);



1.3 其他插入数据的方法

Eloquent模型类还支持其它插入数据的方法——firstOrCreate和firstOrNew，两者都是先通过通过传入属性值在数据库中查找匹配记录，如果没有找到则创建一个新的模型实例，不同之处在于后者不会将数据持久化到数据库，需要调用save方法才行。
2、更新模型
2.1 使用save方法更新模型

save方法还可以用于更新模型，要更新模型数据，先要获取该模型实例，然后修改模型属性，再调用save方法保存即可：

$post = Post::find(1);
$post->title = 'test 1 title';
if($post->save()){
    echo '更新文章成功！';
}else{
    echo '更新文章失败！';
}


2.2 使用update方法更新数据

和create相对应的，Eloquent模型类还支持使用update方法更新数据，同样要用到批量赋值：

$input = [
    'title'=>'test 6 title',
    'content'=>'test content 6',
    'cat_id'=>1,
    'views'=>200,
    'user_id'=>1
];
$post = Post::find(6);
if($post->update($input)){
    echo '更新文章成功！';
    dd($post);
}else{
    echo '更新文章失败！';
}



1、删除模型
1.1 使用delete删除模型

删除模型很简单，先获取要删除的模型实例，然后调用delete方法即可：

$post = Post::find(5);
if($post->delete()){
    echo '删除文章成功！';
}else{
    echo '删除文章失败！';
}

该方法返回true或false。
1.2 使用destroy删除模型

当然如果已知要删除的模型id的话，可以用更简单的方法destroy直接删除：

$deleted = Post::destroy(5);

你也可以一次传入多个模型id删除多个模型：

$deleted = Post::destroy([1,2,3,4,5]);

调用destroy方法返回被删除的记录数。
1.3 使用查询构建器删除模型

既然前面提到Eloquent模型本身就是查询构建器，也可以使用查询构建器风格删除模型，比如我们要删除所有浏览数为0的文章，可以使用如下方式：

$deleted = Models\Post::where('views', 0)->delete();

返回结果为被删除的文章数。

软删除的实现  查询包含软删除的记录    只查询软删除的记录   软删除恢复   强制删除
http://laravelacademy.org/post/1020.html


Eloquent ORM 实例教程 —— 查询作用域和模型事件
http://laravelacademy.org/post/1073.html
**/





















